﻿//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Microsoft Public License.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace AstoriaOverAstoriaTests
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Xml;
    using System.Xml.Linq;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    public static class AtomComparer
    {
        private static void VerifyXAttributeEqual(XAttribute a, XAttribute b)
        {
            Assert.AreEqual(a.Name, b.Name, "The names are different.");
            if (a.Name == XNamespace.Xml + "base") return;
            if (a.IsNamespaceDeclaration) return;
            Assert.AreEqual(a.Value, b.Value, "The values are different.");
        }

        private static void VerifyXTextEqual(XText a, XText b)
        {
            Assert.AreEqual(a.Value, b.Value, "The values are different.");
        }

        private static bool VerifyXElementEqual(XElement a, XElement b)
        {
            Assert.AreEqual(a.Name, b.Name, "The names are different.");
            if (a.Name == AoATestUtil.AtomNs + "id") return false;
            if (a.Name == AoATestUtil.AtomNs + "category") return false;
            if (a.Name == AoATestUtil.AtomNs + "updated") return false;
            return true;
        }

        private static void VerifyXContainerEqual(XContainer a, XContainer b)
        {
            var anodes = a.Nodes().Where(n => n.NodeType == XmlNodeType.Element || n.NodeType == XmlNodeType.Text);
            var bnodes = b.Nodes().Where(n => n.NodeType == XmlNodeType.Element || n.NodeType == XmlNodeType.Text);

            IEnumerator<XNode> ae = anodes.GetEnumerator();
            IEnumerator<XNode> be = bnodes.GetEnumerator();
            while (ae.MoveNext())
            {
                Assert.IsTrue(be.MoveNext());
                VerifyXNodeEqual(ae.Current, be.Current);
            }
            Assert.IsFalse(be.MoveNext());
        }

        private static void VerifyXNodeEqual(XNode nodea, XNode nodeb)
        {
            Assert.AreEqual(nodea.NodeType, nodeb.NodeType, "Node types are different.");

            switch (nodea.NodeType)
            {
                case XmlNodeType.Text:
                    VerifyXTextEqual(nodea as XText, nodeb as XText);
                    break;

                case XmlNodeType.Element:
                    {
                        XElement a = nodea as XElement;
                        XElement b = nodeb as XElement;
                        if (VerifyXElementEqual(a, b))
                        {
                            var attributes = a.Attributes().OrderBy(t => t.Name.ToString()).
                                Join(b.Attributes(), (t) => t.Name, (t) => t.Name, (aa, ab) => new { A = aa, B = ab });
                            foreach (var at in attributes)
                            {
                                VerifyXAttributeEqual(at.A, at.B);
                            }

                            VerifyXContainerEqual(nodea as XContainer, nodeb as XContainer);
                        }
                    }
                    break;

                case XmlNodeType.Document:
                    VerifyXContainerEqual(nodea as XContainer, nodeb as XContainer);
                    break;
            }
        }

        public static void VerifyEqual(XNode a, XNode b)
        {
            try
            {
                if (a == null)
                {
                    Assert.IsNull(b, "The response A is empty, but the response B is not.");
                }
                else
                {
                    VerifyXNodeEqual(a, b);
                }
            }
            catch (Exception e)
            {
                Trace.WriteLine("Response A:");
                Trace.WriteLine(a.ToString());
                Trace.WriteLine("Response B:");
                Trace.WriteLine(b.ToString());
                Assert.Fail(e.Message);
            }
        }
    }
}